/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.channel;

import java.util.ArrayList;
import java.util.List;

import static io.netty.util.internal.ObjectUtil.checkPositive;
import static java.lang.Math.max;
import static java.lang.Math.min;

/**
 * The {@link RecvByteBufAllocator} that automatically increases and
 * decreases the predicted buffer size on feed back.
 * <p>
 * It gradually increases the expected number of readable bytes if the previous
 * read fully filled the allocated buffer.  It gradually decreases the expected
 * number of readable bytes if the read operation was not able to fill a certain
 * amount of the allocated buffer two times consecutively.  Otherwise, it keeps
 * returning the same prediction.
 */
public class AdaptiveRecvByteBufAllocator extends DefaultMaxMessagesRecvByteBufAllocator {

    static final int DEFAULT_MINIMUM = 64;
    static final int DEFAULT_INITIAL = 1024;
    static final int DEFAULT_MAXIMUM = 65536;
    //索引增量   4
    private static final int INDEX_INCREMENT = 4;
    //索引减量   1
    private static final int INDEX_DECREMENT = 1;
    //数组里面存放不同尺寸大小的数字，这个数字就是 guess 方法要用的 。
    private static final int[] SIZE_TABLE;

    static {
        List<Integer> sizeTable = new ArrayList<Integer>();
        //往集合里面添加 16  32  48 ....
        for (int i = 16; i < 512; i += 16) {
            sizeTable.add(i);
        }
        //继续往集合里面添加 1024 2048  ... 直到  integer的最大值  溢出变成负数...
        for (int i = 512; i > 0; i <<= 1) {
            sizeTable.add(i);
        }
        //数组赋值
        SIZE_TABLE = new int[sizeTable.size()];
        for (int i = 0; i < SIZE_TABLE.length; i ++) {
            SIZE_TABLE[i] = sizeTable.get(i);
        }
    }

    //
    @Deprecated
    public static final AdaptiveRecvByteBufAllocator DEFAULT = new AdaptiveRecvByteBufAllocator();

    private static int getSizeTableIndex(final int size) {
        for (int low = 0, high = SIZE_TABLE.length - 1;;) {
            if (high < low) {
                return low;
            }
            if (high == low) {
                return high;
            }

            int mid = low + high >>> 1;
            int a = SIZE_TABLE[mid];
            int b = SIZE_TABLE[mid + 1];
            if (size > b) {
                low = mid + 1;
            } else if (size < a) {
                high = mid - 1;
            } else if (size == a) {
                return mid;
            } else {
                return mid + 1;
            }
        }
    }

    private final class HandleImpl extends MaxMessageHandle {
        private final int minIndex;
        private final int maxIndex;
        private int index;
        private int nextReceiveBufferSize;
        private boolean decreaseNow;

        HandleImpl(int minIndex, int maxIndex, int initial) {
            this.minIndex = minIndex;
            this.maxIndex = maxIndex;
            //计算1024在sizeTable的下标
            index = getSizeTableIndex(initial);
            //表示下一次分配出来的 byteBuf 容量大小。
            //默认第一次分配的是1024。
            nextReceiveBufferSize = SIZE_TABLE[index];
        }

        @Override
        public void lastBytesRead(int bytes) {
            //条件成立说明读取的数据量与评估的数据量一致，说明可能还有数据没读取完，还要继续读取...
            if (bytes == attemptedBytesRead()) {
                //这个方法想要更新nextReceiveBufferSize的大小
                //因为上一次被读满了，可能下一次需要更大的缓冲区。
                record(bytes);
            }
            super.lastBytesRead(bytes);
        }

        @Override
        public int guess() {
            return nextReceiveBufferSize;
        }

        //本次从channel内读取的数据量
        private void record(int actualReadBytes) {
            if (actualReadBytes <= SIZE_TABLE[max(0, index - INDEX_DECREMENT)]) {
                if (decreaseNow) {
                    index = max(index - INDEX_DECREMENT, minIndex);
                    nextReceiveBufferSize = SIZE_TABLE[index];
                    decreaseNow = false;
                } else {
                    decreaseNow = true;
                }
            } else if (actualReadBytes >= nextReceiveBufferSize) {
                index = min(index + INDEX_INCREMENT, maxIndex);
                nextReceiveBufferSize = SIZE_TABLE[index];
                decreaseNow = false;
            }
        }

        @Override
        public void readComplete() {
            //这个方法想要更新totalBytesRead的大小
            //因为上一次被读满了，可能下一次需要更大的缓冲区。
            record(totalBytesRead());
        }
    }

    private final int minIndex;
    private final int maxIndex;
    private final int initial;


    public AdaptiveRecvByteBufAllocator() {
        //参数一 64
        //参数二 1024
        //参数三 65536
        this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM);
    }

    //参数一：最小值  64
    //参数二：初始值  1024
    //参数三：最大值  65536
    public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) {
        checkPositive(minimum, "minimum");
        ///初始值不能小于最小值
        if (initial < minimum) {
            throw new IllegalArgumentException("initial: " + initial);
        }
        //初始值不能大于最大值
        if (maximum < initial) {
            throw new IllegalArgumentException("maximum: " + maximum);
        }

        //使用二分查找算法获取 minimum 在数组中的下标
        //SIZE_TABLE[下标]可能小于等于minimum的值
        int minIndex = getSizeTableIndex(minimum);

        //确保SIZE_TABLE[minimum]>=minimum
        if (SIZE_TABLE[minIndex] < minimum) {
            this.minIndex = minIndex + 1;
        } else {
            this.minIndex = minIndex;
        }
        //使用二分查找的方式，获取 maximum size 在数组内的小下标
        //SIZE_TABLE[下标]可能大于等于maximum的值
        int maxIndex = getSizeTableIndex(maximum);
        //确保值不超出
        if (SIZE_TABLE[maxIndex] > maximum) {
            this.maxIndex = maxIndex - 1;
        } else {
            this.maxIndex = maxIndex;
        }
        //初始值
        this.initial = initial;
    }

    @SuppressWarnings("deprecation")
    @Override
    public Handle newHandle() {
        /*
            参数一：64在SIZE_TABLE数组中的下标
            参数二：65536在SIZE_TABLE数组中的下标
            参数三：1024在SIZE_TABLE数组中的下标
         */
        return new HandleImpl(minIndex, maxIndex, initial);
    }

    @Override
    public AdaptiveRecvByteBufAllocator respectMaybeMoreData(boolean respectMaybeMoreData) {
        super.respectMaybeMoreData(respectMaybeMoreData);
        return this;
    }
}
